﻿using System;
using System.Collections.Generic;
using System.Text;
using NUnit.Framework;
using JsonSerializer = System.Text.Json.JsonSerializer;

namespace nunit_demo
{
    class Method
    {

        [Test]
        public void TestMethodParamOfRef()
        {
            int a = 1;
            this.ChangeVal(a);
            // 输出为1
            Console.WriteLine(a);

            ObjParam param = new ObjParam();
            param.id = 1;
            param.name = "s1";
            this.ChangeVal(param);
            // 输出{"id":1,"name":"s1"}
            Console.WriteLine(param);

            // 从上面的输出结果中可以发现: 无论是基本类型还是引用类型, 作为形参传入函数后, 函数中是无法修改其指向的地址的
        }

        [Test]
        public void TestMethodOfRefParam()
        {
            int a = 1;
            // ref修饰的参数入参也需要添加ref修饰符
            this.ChangeVal2(ref a);
            // 输出为99, 可以发现基本类型修改后同步到了外面
            Console.WriteLine(a);

            ObjParam param = new ObjParam();
            param.id = 1;
            param.name = "s1";
            this.ChangeVal2(ref param);
            // 输出{"id":999,"name":"s999"}
            Console.WriteLine(param);
        }

        /// <summary>
        /// 传入基本类型, 在函数中进行修改, 无法同步到外部
        /// </summary>
        /// <param name="value">待修改的值</param>
        public void ChangeVal(int value)
        {
            value = 333;
        }

        /// <summary>
        /// 传入引用类型, 在函数中进行修改字段, 是可以同步到外部的; 但是直接修改函数的形参指向的对象是无效的
        /// </summary>
        /// <param name="objParam">待修改的对象</param>
        public void ChangeVal(ObjParam objParam)
        {
            ObjParam result = new ObjParam();
            result.id = 999;
            result.name = "s999";
            objParam = result;
        }

        /// <summary>
        /// 使用ref修饰参数的函数
        /// </summary>
        /// <param name="value"></param>
        public void ChangeVal2(ref int value)
        {
            value = 99;
        }

        /// <summary>
        /// 测试使用ref修饰对象这种参数
        /// </summary>
        /// <param name="objParam"></param>
        public void ChangeVal2(ref ObjParam objParam)
        {
            ObjParam result = new ObjParam();
            result.id = 999;
            result.name = "s999";
            objParam = result;
        }

    }

    class MethodOutParam
    {

        [Test]
        public void OutParam()
        {
            int a = 1;
            this.ChangeVal(out a);
            // 输出: 99
            Console.WriteLine(a);

            ObjParam param = new ObjParam();
            param.id = 1;
            param.name = "s1";
            this.ChangeVal(out param);
            // 输出{"id":999,"name":"s999"}
            Console.WriteLine(param);
        }

        /// <summary>
        /// 使用out修饰参数
        /// 注意:
        /// - 被out修饰的参数在编译时会要求离开函数前必须被赋值
        /// </summary>
        /// <param name="value"></param>
        public void ChangeVal(out int value)
        {
            value = 99;
        }

        /// <summary>
        /// 使用out来修饰对象
        /// </summary>
        /// <param name="objParam"></param>
        public void ChangeVal(out ObjParam objParam)
        {
            ObjParam result = new ObjParam();
            result.id = 999;
            result.name = "s999";
            objParam = result;
        }

    }

    /// <summary>
    /// 变长参数测试
    /// </summary>
    class UncertainParamMethod
    {

        [Test]
        public void Test1()
        {
            var sumVal = this.Sum(1, 2, 3, 4);
            Console.WriteLine(sumVal);

            this.MyPrint("小明", 1, 3, 4, 5);
        }

        /// <summary>
        /// 给参数添加params修饰表示这是一个变长的参数
        /// 注意:
        /// - 与java相同, 变长参数必须在函数参数列表的最后一个
        /// </summary>
        /// <param name="value">待求和的变长参数</param>
        /// <returns></returns>
        public int Sum(params int[] value)
        {
            int sum = 0;
            for (int i = 0; i < value.Length; i++)
            {
                sum += value[i];
            }
            return sum;
        }

        /// <summary>
        /// 变长参数只能出现一个且必须在参数列表的最后一个
        /// </summary>
        /// <param name="name"></param>
        /// <param name="arr"></param>
        public void MyPrint(String name, params int[] arr)
        {
            Console.WriteLine(name);
            for (int i = 0; i < arr.Length; i++)
            {
                Console.WriteLine(arr[i]);
            }
        }

    }

    /// <summary>
    /// 测试参数默认值的函数.
    /// </summary>
    class DefaultValParam
    {

        [Test]
        public void DefaultValFunc()
        {
            this.ShowType("s110");
            this.ShowType("s110", 21);
        }

        /// <summary>
        /// 如果不给默认参数传入值, 使用的就是默认值
        /// 注意:
        /// - 默认参数必须放在参数列表的后面
        /// </summary>
        /// <param name="name"></param>
        /// <param name="type"></param>
        public void ShowType(String name, int type = 2)
        {
            Console.WriteLine("type {0}, name {1}", type, name);
        }

    }

    class MethodReload
    {

        public void Show(int type)
        { 

        }

        /// <summary>
        /// 重载之参数相同类型不同
        /// </summary>
        /// <param name="name"></param>
        public void Show(string name)
        {

        }

        /// <summary>
        /// 重载之参数数量不同
        /// </summary>
        /// <param name="type"></param>
        /// <param name="name"></param>
        public void Show(int type, string name)
        {

        }

        /// <summary>
        /// 重载之参数个数相同, 顺序不同
        /// </summary>
        /// <param name="name"></param>
        /// <param name="type"></param>
        public void Show(string name, int type)
        {

        }

        /// <summary>
        /// 重载之使用ref修饰符也算重载
        /// </summary>
        /// <param name="name"></param>
        /// <param name="type"></param>
        public void Show(ref string name, int type)
        {

        }

        /// <summary>
        /// 重载之使用out修饰符也算重载
        /// 注意:
        /// - 已经使用ref重载的函数不能使用out修饰符继续重载
        /// </summary>
        /// <param name="name"></param>
        /// <param name="type"></param>
        public void Show(string name, out int type)
        {
            type = 1;
        }
    }

    /// <summary>
    /// 递归函数的测试
    /// </summary>
    class RecursionMethod
    {
        /// <summary>
        /// 采用递归方式求fib数列的经典算法
        /// </summary>
        /// <param name="f1"></param>
        /// <returns></returns>
        public int Fib(int f1)
        {
            if (f1 < 1)
            {
                throw new Exception("fib数列的数必须是大于0的");
            }
            
            if (f1 == 1) 
            {
                return 1;
            }
            if (f1 == 2)
            {
                return 1;
            }
            return this.Fib(f1 - 1) + this.Fib(f1 - 2);
        }

        [Test]
        public void ShowFib()
        {
            Console.WriteLine(this.Fib(1));
        }

        /// <summary>
        /// 练习题: 传入一个值, 求这个值得阶乘
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public int JieCheng(int value)
        {
            if (value == 1)
            {
                return 1;
            }
            return value * JieCheng(value - 1);
        }

        [Test]
        public void ShowJieCheng()
        {
            Console.WriteLine(this.JieCheng(5));
        }

        /// <summary>
        /// 练习题: 传入一个值, 求这个值得等差数列的阶乘之和
        /// </summary>
        /// <param name="num"></param>
        /// <returns></returns>
        public int JieChengSum(int num)
        {
            if (num == 1)
            {
                return 1;
            }

            return this.JieCheng(num) + this.JieChengSum(num - 1);
        }

        [Test]
        public void ShowJieChengSum()
        {
            Console.WriteLine(this.JieChengSum(10));
        }

        /// <summary>
        /// 练习题: 竹竿长len, 日取其半, 取endDay天后还剩多少
        /// </summary>
        /// <param name="len">竹竿长度</param>
        /// <param name="endDay">取的天数</param>
        /// <returns>剩余长度</returns>
        public float ZhuGan(float len, int endDay)
        {
            if (0 == endDay)
            {
                return len;
            }
            return ZhuGan(len / 2, --endDay);
        }

        [Test]
        public void ShowZhuGan()
        {
            Console.WriteLine(this.ZhuGan(100f, 1));
        }

        /// <summary>
        /// 练习题: 传入一个值, 打印1...这个值得序列
        /// </summary>
        /// <param name="value"></param>
        public void MyPrint(int value) 
        {
            Console.WriteLine(value);
            if (value == 1) 
            {
                return;
            }
            this.MyPrint(--value);
        }

        [Test]
        public void ShowMyPrint()
        {
            this.MyPrint(200);
        }

    }

    class ObjParam 
    {
    
        public int id 
        {
            get;
            set;
        }

        public string name
        {
            get;
            set;
        }

        public override string ToString()
        {
            return JsonSerializer.Serialize(this);
        }

    }
}
